package com.dbflow5.processor.definition.provider;

import com.dbflow5.MapUtils;
import com.dbflow5.contentprovider.annotation.ContentUri;
import com.dbflow5.contentprovider.annotation.Notify;
import com.dbflow5.contentprovider.annotation.NotifyMethod;
import com.dbflow5.contentprovider.annotation.TableEndpoint;
import com.dbflow5.processor.ProcessorManager;
import com.dbflow5.processor.definition.BaseDefinition;
import com.dbflow5.processor.utils.ProcessorUtils;
import com.squareup.javapoet.TypeName;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Description:
 */
public class TableEndpointDefinition extends BaseDefinition {

    public List<ContentUriDefinition> contentUriDefinitions = new ArrayList<>();

    /**
     * Dont want duplicate paths.
     */
    public Map<String, ContentUriDefinition> pathValidationMap = new HashMap<>();

    public Map<String, Map<NotifyMethod, List<NotifyDefinition>>> notifyDefinitionPathMap = new HashMap<>();

    public String tableName = null;

    public TypeName contentProviderName;

    public boolean isTopLevel;

    public TableEndpointDefinition(TableEndpoint tableEndpoint, Element typeElement, ProcessorManager processorManager) {
        super(typeElement, processorManager, processorManager.elements.getPackageOf(typeElement) != null &&
                        processorManager.elements.getPackageOf(typeElement).getQualifiedName() != null?
                processorManager.elements.getPackageOf(typeElement).getQualifiedName().toString()
                : "");

        contentProviderName = ProcessorUtils.extractTypeNameFromAnnotation(tableEndpoint, e -> null, tableEndpoint1 -> {
            tableName = tableEndpoint1.name();
            //tableEndpoint.contentProvider;
            return null;
        });

        isTopLevel = typeElement.getEnclosingElement() instanceof PackageElement;

        List<? extends Element> elements = processorManager.elements.getAllMembers((TypeElement) typeElement);
        for (Element innerElement : elements) {
            ContentUri contentUri = innerElement.getAnnotation(ContentUri.class);
            if (contentUri != null) {
                ContentUriDefinition contentUriDefinition = new ContentUriDefinition(contentUri, innerElement, processorManager);
                if (!pathValidationMap.containsKey(contentUriDefinition.path)) {
                    contentUriDefinitions.add(contentUriDefinition);
                } else {
                    processorManager.logError("There must be unique paths " +
                            "for the specified @ContentUri ${contentUriDefinition.name} " +
                            "from $contentProviderName");
                }
            }
            Notify notify = innerElement.getAnnotation(Notify.class);
            if (innerElement instanceof ExecutableElement) {
                NotifyDefinition notifyDefinition = new NotifyDefinition(notify, (ExecutableElement) innerElement, processorManager);
                for (String path : notifyDefinition.paths) {
                    Map<NotifyMethod, List<NotifyDefinition>> methodListMap = MapUtils.getOrPut(notifyDefinitionPathMap, path, new HashMap<>());
                    List<NotifyDefinition> notifyDefinitionList = MapUtils.getOrPut(methodListMap, notifyDefinition.method, new ArrayList<>());
                    notifyDefinitionList.add(notifyDefinition);
                }
            }
        }
    }
}
